步骤 3:为库添加使用要求

练习 1 - 为库添加使用要求

目标的使用要求参数允许对库或可执行文件的链接和包含行进行更有效的控制,同时还对 CMake 中目标的传递属性提供更多控制。利用使用要求的主要命令有

目标

为库添加使用要求。

有用材料

要编辑的文件

  • MathFunctions/CMakeLists.txt

  • CMakeLists.txt

入门

在本练习中,我们将重构来自Adding a Library的代码以使用现代 CMake 方法。我们将让我们的库定义自己的使用要求,以便根据需要将它们传递到其他目标。在本例中,MathFunctions 将指定任何需要的包含目录。然后,使用目标 Tutorial 只需链接到 MathFunctions 而不必担心任何额外的包含目录。

起始源代码在 Step3 目录中提供。在本练习中,完成 TODO 1TODO 3

首先,在 MathFunctions/CMakeLists 中添加对 target_include_directories() 的调用。请记住,CMAKE_CURRENT_SOURCE_DIR 是当前正在处理的源目录的路径。

然后,更新(并简化!)顶级 CMakeLists.txt 中对 target_include_directories() 的调用。

构建和运行

创建一个名为 Step3_build 的新目录,运行 cmake 可执行文件或 cmake-gui 来配置项目,然后使用您选择的构建工具或使用 cmake --build . 从构建目录进行构建。以下是从命令行执行此操作的简要说明

mkdir Step3_build
cd Step3_build
cmake ../Step3
cmake --build .

接下来,使用新构建的 Tutorial 并验证它是否按预期工作。

解决方案

让我们更新上一步的代码,以使用现代 CMake 方法使用要求。

我们想说明任何链接到 MathFunctions 的人都需要包含当前源目录,而 MathFunctions 本身则不需要。这可以通过 INTERFACE 使用要求来表达。请记住 INTERFACE 表示消费者需要而生产者不需要的内容。

MathFunctions/CMakeLists.txt 的末尾,使用 target_include_directories() 以及 INTERFACE 关键字,如下所示

TODO 1:单击以显示/隐藏答案
TODO 1:MathFunctions/CMakeLists.txt
target_include_directories(MathFunctions
                           INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
                           )

现在我们已经指定了 MathFunctions 的使用要求,我们可以安全地从顶级 CMakeLists.txt 中删除对 EXTRA_INCLUDES 变量的使用。

删除此行

TODO 2:单击以显示/隐藏答案
TODO 2:CMakeLists.txt
list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")

并从 target_include_directories 中删除 EXTRA_INCLUDES

TODO 3:单击以显示/隐藏答案
TODO 3:CMakeLists.txt
target_include_directories(Tutorial PUBLIC
                           "${PROJECT_BINARY_DIR}"
                           )

请注意,使用这种技术,我们的可执行目标唯一使用我们的库的方法是调用 target_link_libraries(),并传入库目标的名称。在大型项目中,手动指定库依赖项的经典方法会很快变得非常复杂。

练习 2 - 使用接口库设置 C++ 标准

现在我们已经将代码切换到更现代的方法,让我们演示一种用于将属性设置为多个目标的现代技术。

让我们重构现有的代码以使用 INTERFACE 库。我们将在下一步中使用该库来演示 generator expressions 的常见用途。

目标

添加一个 INTERFACE 库目标来指定所需的 C++ 标准。

有用资源

要编辑的文件

  • CMakeLists.txt

  • MathFunctions/CMakeLists.txt

入门

在本练习中,我们将重构代码以使用一个 INTERFACE 库来指定 C++ 标准。

从步骤 3 练习 1 结束时留下的内容开始本练习。您将需要完成 TODO 4TODO 7

首先编辑顶层的 CMakeLists.txt 文件。构建一个名为 tutorial_compiler_flagsINTERFACE 库目标,并指定 cxx_std_11 作为目标编译器特性。

修改 CMakeLists.txtMathFunctions/CMakeLists.txt,使所有目标都具有一个 target_link_libraries() 调用到 tutorial_compiler_flags

构建并运行

由于我们已经从练习 1 中配置了构建目录,只需通过调用以下命令重新构建代码即可

cd Step3_build
cmake --build .

接下来,使用新构建的 Tutorial 并验证它是否按预期工作。

解决方案

让我们更新上一步骤中的代码,使用接口库来设置我们的 C++ 要求。

首先,我们需要删除两个 set() 对变量 CMAKE_CXX_STANDARDCMAKE_CXX_STANDARD_REQUIRED 的调用。需要删除的具体行如下所示

CMakeLists.txt
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

接下来,我们需要创建一个接口库,tutorial_compiler_flags。然后使用 target_compile_features() 添加编译器特性 cxx_std_11

TODO 4:点击显示/隐藏答案
TODO 4:CMakeLists.txt
add_library(tutorial_compiler_flags INTERFACE)
target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)

最后,在我们的接口库设置完毕后,我们需要将可执行文件 Tutorial、库 SqrtLibrary 和库 MathFunctions 链接到新的 tutorial_compiler_flags 库。代码分别如下所示

TODO 5:点击显示/隐藏答案

这个

TODO 6:点击显示/隐藏答案

以及这个

TODO 7:点击显示/隐藏答案

这样,我们所有的代码仍然需要 C++ 11 才能构建。但请注意,使用这种方法,我们能够具体指定哪些目标需要特定的要求。此外,我们在接口库中创建了单一的事实来源。